0. Introduction
The purpose of this analysis is to determine which gene sets from MSigDB are most enriched in the genes we previously predicted to contain IREs. This is important as we wanted to see the biological relevance of the IRE-containing genes, and also their overlap in existing gene sets.
The approach is as follows:
- Load in relevant gene set collections from MSigDB.
- Get them into a format suitable for performing Fisher’s exact test to test whether they are enriched for IRE-containing genes.
- Results displayed on a bar chart and also as a network.
The motivation for doing this analysis comes from an interesting observation using the naive approach of searching MSigDB for all gene sets with iron or heme in their name and seeing the overlap in genes between these gene sets and the IRE-containing genesets we identified.
ironSets <- list(
c5_GO_2_IRON_2_SULFUR_CLUSTER_BINDING = c5_mapped$GO_2_IRON_2_SULFUR_CLUSTER_BINDING,
c5_GO_4_IRON_4_SULFUR_CLUSTER_BINDING = c5_mapped$GO_4_IRON_4_SULFUR_CLUSTER_BINDING,
c5_GO_CELLULAR_IRON_ION_HOMEOSTASIS = c5_mapped$GO_CELLULAR_IRON_ION_HOMEOSTASIS,
c5_GO_HEME_METABOLIC_PROCESS = c5_mapped$GO_HEME_METABOLIC_PROCESS,
c5_GO_HEME_BIOSYNTHETIC_PROCESS = c5_mapped$GO_HEME_BIOSYNTHETIC_PROCESS,
c5_GO_HEMOGLOBIN_COMPLEX = c5_mapped$GO_HEMOGLOBIN_COMPLEX,
c5_GO_IRON_COORDINATION_ENTITY_TRANSPORT = c5_mapped$GO_IRON_COORDINATION_ENTITY_TRANSPORT,
c5_GO_IRON_ION_BINDING = c5_mapped$GO_IRON_ION_BINDING,
c5_GO_IRON_ION_HOMEOSTASIS = c5_mapped$GO_IRON_ION_HOMEOSTASIS,
c5_GO_IRON_ION_IMPORT = c5_mapped$GO_IRON_ION_IMPORT,
c5_GO_IRON_ION_TRANSPORT = c5_mapped$GO_IRON_ION_TRANSPORT,
c5_GO_OXIDOREDUCTASE_ACTIVITY_ACTING_ON_A_HEME_GROUP_OF_DONORS = c5_mapped$GO_OXIDOREDUCTASE_ACTIVITY_ACTING_ON_A_HEME_GROUP_OF_DONORS,
c5_GO_RESPONSE_TO_IRON_ION = c5_mapped$GO_RESPONSE_TO_IRON_ION,
h_HEME_METABOLISM = h_mapped$HALLMARK_HEME_METABOLISM,
c2_REACTOME_IRON_UPTAKE_AND_TRANSPORT = c2_mapped$REACTOME_IRON_UPTAKE_AND_TRANSPORT
)
ironSets_idx <- ids2indices(ironSets, rownames(v))
head(ironSets,2)
$c5_GO_2_IRON_2_SULFUR_CLUSTER_BINDING
[1] "ENSDARG00000062780" "ENSDARG00000079516" "ENSDARG00000052703" "ENSDARG00000013044" "ENSDARG00000007745" "ENSDARG00000038154"
[7] "ENSDARG00000051956" "ENSDARG00000052190" "ENSDARG00000003462" "ENSDARG00000045733" "ENSDARG00000043665" "ENSDARG00000026582"
[13] "ENSDARG00000035596" "ENSDARG00000020054" "ENSDARG00000074356" "ENSDARG00000028546" "ENSDARG00000092713" "ENSDARG00000058725"
[19] "ENSDARG00000042159" "ENSDARG00000055240"
$c5_GO_4_IRON_4_SULFUR_CLUSTER_BINDING
[1] "ENSDARG00000055472" "ENSDARG00000021466" "ENSDARG00000074552" "ENSDARG00000010267" "ENSDARG00000086853" "ENSDARG00000042727"
[7] "ENSDARG00000062216" "ENSDARG00000027689" "ENSDARG00000026376" "ENSDARG00000036438" "ENSDARG00000074889" "ENSDARG00000004952"
[13] "ENSDARG00000035074" "ENSDARG00000052721" "ENSDARG00000051986" "ENSDARG00000092713" "ENSDARG00000077698" "ENSDARG00000021985"
[19] "ENSDARG00000104848" "ENSDARG00000110572" "ENSDARG00000042881" "ENSDARG00000004517" "ENSDARG00000062987" "ENSDARG00000054821"
[25] "ENSDARG00000038154" "ENSDARG00000051956" "ENSDARG00000011072" "ENSDARG00000055653" "ENSDARG00000058801" "ENSDARG00000007526"
[31] "ENSDARG00000026582" "ENSDARG00000035596" "ENSDARG00000058533" "ENSDARG00000022845" "ENSDARG00000038834" "ENSDARG00000028546"
[37] "ENSDARG00000045308" "ENSDARG00000007294" "ENSDARG00000078479" "ENSDARG00000074410"
head(ironSets_idx,2)
$c5_GO_2_IRON_2_SULFUR_CLUSTER_BINDING
[1] 392 1152 2009 3261 4076 4317 5266 5874 6672 7030 7494 7669 7725 7829 9213 10372 11992 13712
$c5_GO_4_IRON_4_SULFUR_CLUSTER_BINDING
[1] 582 666 1075 1117 1553 1678 3513 3591 3669 4053 4076 4219 4317 5167 5266 5472 5874 5996 6790 6835 7375
[22] 7669 7675 7834 8248 8392 8444 9159 9231 10196 10441 12008 12054 12192 13108 13371 14022 18609
We used a gene set enrichment analysis approach to test for whether these iron-related gene sets were enriched in our DE results.
source(here("R","GSEA","combinedGSEA.R"))
gseaResults_ironSets <- combinedGSEA(v, ironSets_idx, design, contrasts)
# gseaResults_ironSets %>% saveRDS(here("R","GSEA","results","gseaResults_ironSets.rds"))
Results for 6 month, normoxia, mutant vs. wild type below indicate that while some iron-related gene sets show enrichment in this comparison, others do not.
gseaResults_ironSets$combTest$normoxia_6mth_mutant_vs_wt
Overall, the figure below indicates poor overlap between existing gene sets and our IRE-containing gene sets. However, to really see how our IRE genesets relate to existing gene sets, we need to do a more comprehensive test including more of the gene sets not restricted to ones related to iron metabolism.
# Create a dataframe that includes overlap between iron-related gene sets and the ireGenes.
testx <- ironSets %>% lapply(function(x){
overlap <- list(
with3ire = sum(x %in% ireGenes$ire3_all),
with5ire = sum(x %in% ireGenes$ire5_all),
noIre = sum(!(x %in% ireGenes$ire3_all | x %in% ireGenes$ire5_all))
)
}) %>% lapply(function(x){
x %>% bind_rows
}) %>%
do.call("rbind",.) %>%
mutate(geneset = names(ironSets)) %>%
bind_rows(data.frame(
with3ire = length(ireGenes$ire3_all),
with5ire = length(ireGenes$ire5_all),
noIre = 0,
geneset = "- GRCz11 Zebrafish Genes - All Predicted IREs"
)) %>%
bind_rows(data.frame(
with3ire = ireUtr3 %>% as.data.frame %>% dplyr::filter(quality == "High") %>% use_series("gene_id") %>% unique %>% length,
with5ire = ireUtr5 %>% as.data.frame %>% dplyr::filter(quality == "High") %>% use_series("gene_id") %>% unique %>% length,
noIre = 0,
geneset = "- GRCz11 Zebrafish Genes - High Quality Predicted IREs"
)) %>%
mutate(geneset = gsub(x = geneset, pattern = "_", replacement = " "))%>%
melt
binding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorUsing geneset as id variables
# Create a stacked bar chart
compPlot <- testx %>% ggplot(aes(x = str_to_title(stringr::str_wrap(geneset, 20)),
y = value,
fill = variable)) +
geom_col() +
theme(aspect.ratio = 0.7,
axis.text.x = element_text(size= 35),
axis.title.x = element_text(size= 30),
axis.title.y = element_text(size = 30),
legend.text = element_text(size = 20),
legend.title = element_text(size=30),
axis.text.y = element_text(color = "grey20", size = 16)) +
coord_flip() + # Axis labels too long to be readable so this helps.
scale_fill_manual(values=c("#FBB829", "#FF0066", "#dddddd"), labels =c("3' IRE", "5' IRE", "No IRE")) +
labs(y = "Number of genes", x = "Gene set", fill = "Gene\nhas\nIRE?")
compPlot

1. Defining Relevant Gene Sets
We will be testing whether our IRE gene sets are enriched in any of the gene sets in the following gene set collections from MSigDB:
- Hallmark gene sets (H) are coherently expressed signatures derived by aggregating many MSigDB gene sets to represent well-defined biological states or processes.
- Curated gene sets (C2) are from online pathway databases, publications in PubMed, and knowledge of domain experts.
- Motif gene sets (C3) are based on conserved cis-regulatory motifs from a comparative analysis of the human, mouse, rat, and dog genomes.
- Gene ontology gene sets (C5) consist of genes annotated by the same GO terms.
The gene set collections mapped to zebrafish Ensembl IDs have been pre-loaded for this analysis. Each collection is a named list of gene sets, with each gene set containing Ensembl IDs of genes in that set. We will convert all lists into list column structures used in the tibble package.
h_tib <- h_mapped %>% tibble %>% set_colnames(c("ids")) %>% mutate(geneset = names(h_mapped), source = "h")
c2_tib <- c2_mapped %>% tibble %>% set_colnames(c("ids")) %>% mutate(geneset = names(c2_mapped), source = "c2")
c3_tib <- c3_mapped %>% tibble %>% set_colnames(c("ids")) %>% mutate(geneset = names(c3_mapped), source = "c3")
c5_tib <- c5_mapped %>% tibble %>% set_colnames(c("ids")) %>% mutate(geneset = names(c5_mapped), source = "c5")
gs <- bind_rows(h_tib, c2_tib, c3_tib, c5_tib)
gs
We previously loaded the ireGenes R object, which has the following structure:
ireGenes%>%str
List of 4
$ ire3_all: chr [1:1207] "ENSDARG00000102878" "ENSDARG00000070477" "ENSDARG00000006038" "ENSDARG00000063481" ...
$ ire5_all: chr [1:393] "ENSDARG00000045911" "ENSDARG00000017386" "ENSDARG00000045886" "ENSDARG00000015921" ...
$ ire3_hq : chr [1:106] "ENSDARG00000074223" "ENSDARG00000024874" "ENSDARG00000110878" "ENSDARG00000089296" ...
$ ire5_hq : chr [1:49] "ENSDARG00000045911" "ENSDARG00000101351" "ENSDARG00000100006" "ENSDARG00000015551" ...
allIREGenes <- c(ireGenes$ire3_all, ireGenes$ire5_all)
allIREGenes %>% head
[1] "ENSDARG00000102878" "ENSDARG00000070477" "ENSDARG00000006038" "ENSDARG00000063481" "ENSDARG00000045842" "ENSDARG00000095332"
2. Compute gene set overlap with IRE genesets
We wish to see the overlap between the genesets in gs with the ireGenes (including 3’ and 5’ IRE genes), as well as separately, with the 3’ IRE genes and 5’ IRE genes. We will add this information into the gs tibble.
gs %<>% rowwise() %>% mutate(
n = length(ids), # Number of genes in the geneset
n_with_ire = sum(ids %in% allIREGenes), # Number of genes in the gene set which have 3' or 5' predicted IREs
n_without_ire = (n - n_with_ire),
universe_with_ire = sum(rownames(v) %in% allIREGenes & !(rownames(v) %in% ids)),
universe_without_ire = (length(rownames(v)) - universe_with_ire),
n_with_ire3 = sum(ids %in% ireGenes$ire3_all), # Number of genes in the gene set which have 3' predicted IREs
n_without_ire3= (n - n_with_ire),
universe_with_ire3 = sum(rownames(v) %in% ireGenes$ire3_all & !(rownames(v) %in% ids)),
universe_without_ire3 = (length(rownames(v)) - universe_with_ire),
n_with_ire5 = sum(ids %in% ireGenes$ire5_all), # Number of genes in the gene set which have 5' predicted IREs
n_without_ire5= (n - n_with_ire),
universe_with_ire5 = sum(rownames(v) %in% ireGenes$ire5_all & !(rownames(v) %in% ids)),
universe_without_ire5 = (length(rownames(v)) - universe_with_ire)
) %>% ungroup()
gs
3. Contingency tables
Create contingency table for each gene set. We will store this in a list of matrices gs_mat. The 3’ and 5’ contingency matrices are stored in gs_mat3 and gs_mat5.
gs_mat <- gs %>%
dplyr::select(n_with_ire,
n_without_ire,
universe_with_ire,
universe_without_ire) %>%
apply(X = ., MARGIN = 1, FUN = function(x){
x %>%
matrix(2,2) %>%
t %>%
list()
}) %>% set_names(gs$geneset) %>%
lapply(function(x){
x %>% .[[1]]
})
gs_mat3 <- gs %>%
dplyr::select(n_with_ire3,
n_without_ire3,
universe_with_ire3,
universe_without_ire3) %>%
apply(X = ., MARGIN = 1, FUN = function(x){
x %>%
matrix(2,2) %>%
t %>%
list()
}) %>% set_names(gs$geneset) %>%
lapply(function(x){
x %>% .[[1]]
})
gs_mat5 <- gs %>%
dplyr::select(n_with_ire5,
n_without_ire5,
universe_with_ire5,
universe_without_ire5) %>%
apply(X = ., MARGIN = 1, FUN = function(x){
x %>%
matrix(2,2) %>%
t %>%
list()
}) %>% set_names(gs$geneset) %>%
lapply(function(x){
x %>% .[[1]]
})
gs_mat[1:3]
$HALLMARK_TNFA_SIGNALING_VIA_NFKB
[,1] [,2]
[1,] 15 243
[2,] 1433 18274
$HALLMARK_HYPOXIA
[,1] [,2]
[1,] 19 241
[2,] 1430 18277
$HALLMARK_CHOLESTEROL_HOMEOSTASIS
[,1] [,2]
[1,] 5 86
[2,] 1443 18264
gs_mat3[1:3]
$HALLMARK_TNFA_SIGNALING_VIA_NFKB
[,1] [,2]
[1,] 9 243
[2,] 1119 18274
$HALLMARK_HYPOXIA
[,1] [,2]
[1,] 14 241
[2,] 1115 18277
$HALLMARK_CHOLESTEROL_HOMEOSTASIS
[,1] [,2]
[1,] 4 86
[2,] 1124 18264
gs_mat5[1:3]
$HALLMARK_TNFA_SIGNALING_VIA_NFKB
[,1] [,2]
[1,] 6 243
[2,] 344 18274
$HALLMARK_HYPOXIA
[,1] [,2]
[1,] 7 241
[2,] 343 18277
$HALLMARK_CHOLESTEROL_HOMEOSTASIS
[,1] [,2]
[1,] 2 86
[2,] 348 18264
4. Fisher’s exact test
On each contingency table, we will run Fisher’s exact test. We then apply FDR correction to adjust the raw p-values for multiple testing.
fisher_res <- gs_mat %>% lapply(function(x){
x %>% fisher.test()
})
fisher_res3 <- gs_mat3 %>% lapply(function(x){
x %>% fisher.test()
})
fisher_res5 <- gs_mat5 %>% lapply(function(x){
x %>% fisher.test()
})
fisher_res_p <- fisher_res %>% lapply(function(x){x$p.value})
fisher_res_p3 <- fisher_res3 %>% lapply(function(x){x$p.value})
fisher_res_p5 <- fisher_res5 %>% lapply(function(x){x$p.value})
gs %<>%
mutate(fisher_p = fisher_res_p%>%unlist%>%unname,
fisher_p_3 = fisher_res_p3%>%unlist%>%unname,
fisher_p_5 = fisher_res_p5%>%unlist%>%unname) %>%
mutate(fdr = p.adjust(fisher_p, "fdr"),
fdr_3 = p.adjust(fisher_p_3, "fdr"),
fdr_5 = p.adjust(fisher_p_5, "fdr"))
5. Calculate Expected and Observed
For each gene set, we will calculate the number of genes expected to have IREs (based on the background proportion) and observed value. This information will be added to the gs object.
gs %<>% rowwise() %>% mutate(
exp_allIRE = (universe_with_ire / universe_without_ire)*n_without_ire,
obs_allIRE = n_with_ire,
obs_greater_than_exp_allIRE = obs_allIRE > exp_allIRE,
exp_ire3 = (universe_with_ire3 / universe_without_ire3)*n_without_ire3,
obs_ire3 = n_with_ire3,
obs_greater_than_exp_ire3 = obs_ire3 > exp_ire3,
exp_ire5 = (universe_with_ire5 / universe_without_ire5)*n_without_ire5,
obs_ire5 = n_with_ire5,
obs_greater_than_exp_ire5 = obs_ire5 > exp_ire5
)
gs %>%
dplyr::select(geneset, contains("exp"), starts_with("obs"), ) %>% ungroup()
6. Results
The following table shows the top 50 gene sets enriched in IRE genesets, ranked by Fisher’s exact test p-values:
gs %>% ungroup() %>%
arrange(fisher_p) %>%
dplyr::select(geneset, contains("fdr"), n_with_ire, n, starts_with("obs_greater")) %>%
head(50)
Sorted by genesets most enriched in 3’ IRE genes:
gs %>% ungroup %>% arrange(fisher_p_3) %>% dplyr::select(geneset, contains("fdr"), n_with_ire3, n, starts_with("obs_greater")) %>% head(50)
Sorted by genesets most enriched in 5’ IRE genes:
gs %>% ungroup %>% arrange(fisher_p_5) %>% dplyr::select(geneset, contains("fdr"), n_with_ire5, n, starts_with("obs_greater")) %>% head(50)
7. Visualisation (Stacked bar chart)
The following stacked bar chart shows the overlap between the top ~20 genesets and the predicted-IRE genesets.
overlapDf <- gs %>% arrange(fisher_p) %>% dplyr::filter(fdr < 0.1 | fdr_3 < 0.1 | fdr_5 < 0.1) %>%
dplyr::filter(obs_greater_than_exp_allIRE == TRUE | obs_greater_than_exp_ire3 == TRUE | obs_greater_than_exp_ire5 == TRUE) %>%
dplyr::select(geneset, source, n_with_ire3, n_with_ire5, n_without_ire) %>%
bind_rows(data.frame(
geneset = c("- GRCz11 Zebrafish Genes - All Predicted IREs"),
source = c("sires"),
n_with_ire3 = c(length(ireGenes$ire3_all)),
# ireUtr3 %>% as.data.frame %>% dplyr::filter(quality == "High") %>% use_series("gene_id") %>% unique %>% length),
n_with_ire5 = c(length(ireGenes$ire5_all)),
# ireUtr5 %>% as.data.frame %>% dplyr::filter(quality == "High") %>% use_series("gene_id") %>% unique %>% length),
n_without_ire = c(0)
)) %>% dplyr::mutate(geneset = paste0(geneset, " ", source)) %>%
dplyr::mutate(geneset = gsub(x=geneset, pattern = "_", replacement = " ")) %>%
dplyr::select(-source) %>% melt
binding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorUsing geneset as id variables
overlapPlot <- overlapDf %>% ggplot(aes(x = str_to_title(stringr::str_wrap(geneset, 23)),
y = value,
fill = variable)) +
geom_col() +
theme(aspect.ratio = 0.7,
axis.text.x = element_text(size= 35),
axis.title.x = element_text(size= 30),
axis.title.y = element_text(size = 30),
legend.text = element_text(size = 20),
legend.title = element_text(size=30),
axis.text.y = element_text(color = "grey20", size = 13)) +
coord_flip() + # Axis labels too long to be readable so this helps.
scale_fill_manual(values=c("#FBB829", "#FF0066", "#dddddd"), labels =c("3' IRE", "5' IRE", "No IRE")) +
labs(y = "Number of genes", x = "Gene set", fill = "Gene\nhas\nIRE?")
overlapPlot

# export::graph2ppt(overlapPlot, here("R","GSEA","fig","overlapPlot"))
8. Visualisation (Network)
Although the stacked bar chart shows overlap between different gene sets with the predicted IRE geneset, along with information about the size of each geneset, crucially, it doesn’t indicate which genes are in common between the different gene sets. My first idea was pairwise Venn diagrams between the IRE genes in each pair of genesets, but this isn’t the most visual / intuitive way to present the information and it doesn’t highlight the genes which are most often shared between different genesets. Steve had the idea to represent this as a network, with distinct groups of genes (grey) representing genesets, IRE genes in colour, and edges representing overlap between genesets.
8.1. Create the nodes table
I will need to create a data.frame with the following columns:
- Id: Gene ID
- IRE: Factor which can either be
3' IRE, 5' IRE, or no IRE.
- Geneset: The gene set which it belongs to.
nodes<- gs %>% arrange(fisher_p) %>% dplyr::filter(fdr < 0.1 | fdr_3 < 0.1 | fdr_5 < 0.1) %>%
dplyr::filter(obs_greater_than_exp_allIRE == TRUE | obs_greater_than_exp_ire3 == TRUE | obs_greater_than_exp_ire5 == TRUE) %>%
ungroup
# Append information about 3' and 5' IRE genesets
nodes %<>% bind_rows(
tibble(
ids = c(list(ireGenes$ire3_all, ireGenes$ire5_all)),
geneset = c("Predicted 3' IRE genes", "Predicted 5' IRE genes"),
source = c("sires","sires"),
n = c(length(ireGenes$ire3_all), length(ireGenes$ire5_all))
)
)
# Append the Hallmark Heme Metabolism geneset
# Although this gene set wasnt significantly enriched in
# IRE-containing genes, it is probably the most comprehensive
# gene set specifically on heme metabolism, and combines info
# from various studies.
nodes %<>% bind_rows(
h_tib %>% filter(geneset == "HALLMARK_HEME_METABOLISM") %>% mutate(n = length(ids[[1]]))
)
nodes
First we filtered gs for only the genesets that are highly ranked in being enriched for predicted IRE genes. We applied the additional filtering step that the observed number of IRE genes is greater than the expected number. This results in 18 genesets.
The next step is to create all possible combinations of the genesets. We will use the combn function to do this. Then we will create a column common_ids to store the genes which are common to both gene sets being intersected.
gsComb <- combn(nodes$geneset, m = 2) %>% t %>% as.data.frame %>%
set_colnames(c("geneset", "geneset_2b")) %>%
left_join(nodes%>%dplyr::select(ids, geneset), by = "geneset") %>%
dplyr::rename(geneset_nm = geneset,
geneset = geneset_2b) %>%
left_join(nodes%>%dplyr::select(ids, geneset), by = "geneset") %>%
as_tibble %>%
mutate(common_ids = map2(ids.x, ids.y, ~intersect(.x,.y)))
Column `geneset` joining factor and character vector, coercing into character vectorColumn `geneset` joining factor and character vector, coercing into character vector
gsComb
We can extract genes which appear the most often in genesets as follows:
gsComb$common_ids%>%unlist %>% table %>% as.data.frame%>% arrange(desc(Freq)) %>% set_colnames(c("ensembl_gene_id", "f")) %>% left_join(v$genes %>% dplyr::select(-entrezid)) %>% as_tibble
Joining, by = "ensembl_gene_id"
Column `ensembl_gene_id` joining factor and character vector, coercing into character vector
The nodes table will contain
- All genes in the genesets
- The geneset names
pathways <- nodes$geneset %>% as.data.frame %>% set_colnames("label")
genes <- nodes$ids %>% unlist %>% unique %>% as.data.frame %>% set_colnames("label")
nodesDf <- full_join(pathways, genes, by = "label") %>% rowid_to_column("id")
Column `label` joining factors with different levels, coercing to character vector
# nodesDf %>%
# mutate(text = ifelse(id < 18, label, NA)) %>%
# mutate(size = ifelse(id < 16, 2, 1)) %>%
# mutate(colour = ifelse(id < 16, rainbow(26)[id], NA))
head(nodesDf,20)
id label
1 1 TTGCWCAAY_CEBPB_02
2 2 TGANTCA_AP1_C
3 3 YRTCANNRCGC_UNKNOWN
4 4 TGGAAA_NFAT_Q4_01
5 5 KEGG_GLIOMA
6 6 PID_BETA_CATENIN_NUC_PATHWAY
7 7 DACOSTA_UV_RESPONSE_VIA_ERCC3_DN
8 8 WTGAAAT_UNKNOWN
9 9 GO_INTRACELLULAR_SIGNAL_TRANSDUCTION
10 10 CTACTAG_MIR325
11 11 TGGNNNNNNKCCAR_UNKNOWN
12 12 GO_POSTSYNAPTIC_MEMBRANE
13 13 GO_SYNAPTIC_MEMBRANE
14 14 GO_POSITIVE_REGULATION_OF_BLOOD_VESSEL_ENDOTHELIAL_CELL_MIGRATION
15 15 TNCATNTCCYR_UNKNOWN
16 16 Predicted 3' IRE genes
17 17 Predicted 5' IRE genes
18 18 HALLMARK_HEME_METABOLISM
19 19 ENSDARG00000009418
20 20 ENSDARG00000029764
nodesDf2 <- nodesDf %>%
mutate(
ire = case_when(
id < 16 ~ " ",
id == 18 ~ " ",
id == 16 ~ "3",
id == 17 ~ "5",
label %in% ireGenes$ire3_all ~ "3",
label %in% ireGenes$ire5_all ~ "5",
!(label %in% allIREGenes) ~ "no IRE"
)
)%>%
dplyr::select(-id) %>% dplyr::rename(Id = label)
head(nodesDf2, 30)
Id ire
1 TTGCWCAAY_CEBPB_02
2 TGANTCA_AP1_C
3 YRTCANNRCGC_UNKNOWN
4 TGGAAA_NFAT_Q4_01
5 KEGG_GLIOMA
6 PID_BETA_CATENIN_NUC_PATHWAY
7 DACOSTA_UV_RESPONSE_VIA_ERCC3_DN
8 WTGAAAT_UNKNOWN
9 GO_INTRACELLULAR_SIGNAL_TRANSDUCTION
10 CTACTAG_MIR325
11 TGGNNNNNNKCCAR_UNKNOWN
12 GO_POSTSYNAPTIC_MEMBRANE
13 GO_SYNAPTIC_MEMBRANE
14 GO_POSITIVE_REGULATION_OF_BLOOD_VESSEL_ENDOTHELIAL_CELL_MIGRATION
15 TNCATNTCCYR_UNKNOWN
16 Predicted 3' IRE genes 3
17 Predicted 5' IRE genes 5
18 HALLMARK_HEME_METABOLISM
19 ENSDARG00000009418 5
20 ENSDARG00000029764 no IRE
21 ENSDARG00000077842 no IRE
22 ENSDARG00000040184 no IRE
23 ENSDARG00000026723 no IRE
24 ENSDARG00000033498 no IRE
25 ENSDARG00000071168 no IRE
26 ENSDARG00000069356 no IRE
27 ENSDARG00000075899 no IRE
28 ENSDARG00000111712 no IRE
29 ENSDARG00000090624 no IRE
30 ENSDARG00000055543 no IRE
# nodesDf2 %>% write_tsv(here("R","GSEA","data","nodes2.tsv"))
Edges will be between:
- The genesets and all genes within the geneset
- Genes in multiple genesets
edgeDf <- nodes$ids %>% set_names(nodes$geneset) %>% plyr::ldply(data.frame) %>%
set_colnames(c("pathway", "gene")) %>%
left_join(nodesDf, by = c("pathway"="label")) %>%
dplyr::rename(from = id) %>%
left_join(nodesDf, by = c("gene"="label")) %>%
dplyr::rename(to=id) %>%
#dplyr::select(from, to)
dplyr::select(pathway, gene) %>%
set_colnames(c("Source","Target"))
Column `gene`/`label` joining factor and character vector, coercing into character vector
edgeDf %>% head(20)
Source Target
1 TTGCWCAAY_CEBPB_02 ENSDARG00000009418
2 TTGCWCAAY_CEBPB_02 ENSDARG00000029764
3 TTGCWCAAY_CEBPB_02 ENSDARG00000077842
4 TTGCWCAAY_CEBPB_02 ENSDARG00000040184
5 TTGCWCAAY_CEBPB_02 ENSDARG00000026723
6 TTGCWCAAY_CEBPB_02 ENSDARG00000033498
7 TTGCWCAAY_CEBPB_02 ENSDARG00000071168
8 TTGCWCAAY_CEBPB_02 ENSDARG00000069356
9 TTGCWCAAY_CEBPB_02 ENSDARG00000075899
10 TTGCWCAAY_CEBPB_02 ENSDARG00000111712
11 TTGCWCAAY_CEBPB_02 ENSDARG00000090624
12 TTGCWCAAY_CEBPB_02 ENSDARG00000055543
13 TTGCWCAAY_CEBPB_02 ENSDARG00000104279
14 TTGCWCAAY_CEBPB_02 ENSDARG00000005416
15 TTGCWCAAY_CEBPB_02 ENSDARG00000077948
16 TTGCWCAAY_CEBPB_02 ENSDARG00000077749
17 TTGCWCAAY_CEBPB_02 ENSDARG00000051783
18 TTGCWCAAY_CEBPB_02 ENSDARG00000087517
19 TTGCWCAAY_CEBPB_02 ENSDARG00000014181
20 TTGCWCAAY_CEBPB_02 ENSDARG00000004843
Export results
gs %>% saveRDS(here("R","GSEA","results","gs.rds"))
gs %>% ungroup() %>% write.xlsx(here("R","GSEA","results","gs.xlsx"))
# Export significant genesets / of interest
gs %>%ungroup %>%
arrange(fisher_p) %>%
dplyr::filter(fdr < 0.1 | fdr_3 < 0.1 | fdr_5 < 0.1) %>%
dplyr::filter(obs_greater_than_exp_allIRE == TRUE | obs_greater_than_exp_ire3 == TRUE | obs_greater_than_exp_ire5 == TRUE) %>%
dplyr::select(geneset, source, n, n_with_ire3, n_with_ire5, n_without_ire, contains("fisher_p"), contains("fdr")) %>%
write.xlsx(here("R","GSEA","results","gs_topRanked.xlsx"))
9. Human Data
- Managed to get the sets of human genes containing IREs:
zebrafishIreGenes <- readRDS(here::here("R/GSEA/data/ireGenes.rds"))
humanIreGenes <- readRDS(here::here("R/IREGenes/data/human_ireGenes.rds"))
TODO
- [x] Fisher’s test separately on 3’ IRE genes and 5’ IRE genes.
- [x] Stacked bar chart to indicate overlap of the top ~20 genesets with predicted IRE genes.
- [ ] Repeat this but with human / mouse IREs and their genesets to do a cross-species comparison.
- [x] Add in calculation of Expected and Observed IRE gene values and filter significant results to have Observed > Expected.
- [x] Export nodes and edges table for network visualisation.
- [x] Export edges and nodes table again except with Hallmark Heme metabolism added in
Session Info
sessionInfo()
LS0tCnRpdGxlOiAiQXJlIElSRSBnZW5lIHNldHMgb3Zlci1yZXByZXNlbnRlZCBpbiBNU2lnREIgZ2VuZSBzZXRzPyIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogcGFnZWQKLS0tCgpgYGB7ciBTZXR1cCwgaW5jbHVkZT1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBMb2FkIHBhY2thZ2VzCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkocmVhZHIpCmxpYnJhcnkobWFncml0dHIpCmxpYnJhcnkobGltbWEpCmxpYnJhcnkoaGVyZSkKbGlicmFyeShvcGVueGxzeCkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHN0cmluZ3IpCmxpYnJhcnkocHVycnIpCmxpYnJhcnkoZXhwb3J0KQpsaWJyYXJ5KHJlc2hhcGUyKQpsaWJyYXJ5KGZnc2VhKQpsaWJyYXJ5KHRpYmJsZSkKCiMgZ2dwbG90MiB0aGVtZQp0aGVtZV9zZXQodGhlbWVfYncoKSkKCiMgTG9hZCBvYmplY3RzIHJlcXVpcmVkIGZvciB0aGlzIGFuYWx5c2lzLiAKIyBHZW5lIHNldCBjb2xsZWN0aW9ucyB3aXRoIGh1bWFuIGVudHJlemdlbmVzIGNvbnZlcnRlZCB0byB6ZWJyYWZpc2ggRW5zZW1ibCBJRHMgYW5kIHNhdmVkIGFzIGxpc3RzIG9mIGdlbmUgc2V0cy4gCmhfbWFwcGVkIDwtIHJlYWRSRFMoaGVyZSgiUiIsIkdTRUEiLCJkYXRhIiwiZW5zX2hfbWFwcGVkLnJkcyIpKSAgIyBIYWxsbWFyayBHZW5lIHNldCBjb2xsZWN0aW9uIApjMl9tYXBwZWQgPC0gcmVhZFJEUyhoZXJlKCJSIiwiR1NFQSIsImRhdGEiLCJlbnNfYzJfbWFwcGVkLnJkcyIpKSAgIyBDdXJhdGVkIEdlbmUgc2V0IGNvbGxlY3Rpb24KYzNfbWFwcGVkIDwtIHJlYWRSRFMoaGVyZSgiUiIsIkdTRUEiLCJkYXRhIiwiZW5zX2MzX21hcHBlZC5yZHMiKSkgICMgTW90aWYgR2VuZSBzZXQgY29sbGVjdGlvbgpjNV9tYXBwZWQgPC0gcmVhZFJEUyhoZXJlKCJSIiwiR1NFQSIsImRhdGEiLCJlbnNfYzVfbWFwcGVkLnJkcyIpKSAgIyBHZW5lIE9udG9sb2d5IEdlbmUgc2V0IGNvbGxlY3Rpb24gCmlyZUdlbmVzIDwtIHJlYWRSRFMoaGVyZSgiUiIsIkdTRUEiLCJkYXRhIiwiaXJlR2VuZXMucmRzIikpICMgTGlzdCBvZiBJUkUgZ2VuZSBzZXRzLCBjb250YWlucyA0IGdlbmUgc2V0czogMycgQWxsIElSRSBnZW5lcywgNScgQWxsIElSRSBnZW5lcywgMycgSGlnaC1RdWFsaXR5IElSRSBnZW5lcywgNScgSGlnaC1RdWFsaXR5IElSRSBnZW5lcyAKCiMgSVJFIGdlbmVzIEdSYW5nZXMgb2JqZWN0cyBleHBvcnRlZCBmcm9tIFNJUkVzIHdlYiBzZXJ2ZXIKaXJlVXRyMyA8LSByZWFkUkRTKGhlcmUoIlIiLCJJUkVHZW5lcyIsImRhdGEiLCJpcmVVdHIzLnJkcyIpKQppcmVVdHI1IDwtIHJlYWRSRFMoaGVyZSgiUiIsIklSRUdlbmVzIiwiZGF0YSIsImlyZVV0cjUucmRzIikpCgojIERFIGFuYWx5c2lzIG9iamVjdHMKdiA8LSByZWFkUkRTKGhlcmUoIlIiLCJERSIsImRhdGEiLCJ2b29tRGF0YV9nLnJkcyIpKSAgIyB2b29tIG9iamVjdApkZXNpZ24gPC0gcmVhZFJEUyhoZXJlKCJSIiwiREUiLCJkYXRhIiwiZGVzaWduX2cucmRzIikpICAjIERlc2lnbiBtYXRyaXgKY29udHJhc3RzIDwtIHJlYWRSRFMoaGVyZSgiUiIsIkRFIiwiZGF0YSIsImNvbnRyYXN0c19nLnJkcyIpKSAgIyBDb250cmFzdHMgbWF0cml4CgpgYGAKCgojIyAwLiBJbnRyb2R1Y3Rpb24KClRoZSBwdXJwb3NlIG9mIHRoaXMgYW5hbHlzaXMgaXMgdG8gZGV0ZXJtaW5lIHdoaWNoIGdlbmUgc2V0cyBmcm9tIFtNU2lnREJdKGh0dHA6Ly9zb2Z0d2FyZS5icm9hZGluc3RpdHV0ZS5vcmcvZ3NlYS9tc2lnZGIvaW5kZXguanNwKSAKYXJlIG1vc3QgZW5yaWNoZWQgaW4gdGhlIGdlbmVzIHdlIHByZXZpb3VzbHkgcHJlZGljdGVkIHRvIGNvbnRhaW4gSVJFcy4gVGhpcyBpcyBpbXBvcnRhbnQgYXMgd2Ugd2FudGVkIHRvIHNlZSB0aGUgYmlvbG9naWNhbCByZWxldmFuY2Ugb2YgdGhlIElSRS1jb250YWluaW5nIGdlbmVzLCBhbmQgYWxzbyB0aGVpciBvdmVybGFwIGluIGV4aXN0aW5nIGdlbmUgc2V0cy4gCgpUaGUgYXBwcm9hY2ggaXMgYXMgZm9sbG93czoKCi0gTG9hZCBpbiByZWxldmFudCBnZW5lIHNldCBjb2xsZWN0aW9ucyBmcm9tIE1TaWdEQi4KLSBHZXQgdGhlbSBpbnRvIGEgZm9ybWF0IHN1aXRhYmxlIGZvciBwZXJmb3JtaW5nIEZpc2hlcidzIGV4YWN0IHRlc3QgdG8gdGVzdCB3aGV0aGVyIHRoZXkgYXJlIGVucmljaGVkIGZvciBJUkUtY29udGFpbmluZyBnZW5lcy4gCi0gUmVzdWx0cyBkaXNwbGF5ZWQgb24gYSBiYXIgY2hhcnQgYW5kIGFsc28gYXMgYSBuZXR3b3JrLiAKClRoZSBtb3RpdmF0aW9uIGZvciBkb2luZyB0aGlzIGFuYWx5c2lzIGNvbWVzIGZyb20gYW4gaW50ZXJlc3Rpbmcgb2JzZXJ2YXRpb24gdXNpbmcgdGhlIG5haXZlIGFwcHJvYWNoIG9mIHNlYXJjaGluZyBNU2lnREIgZm9yIGFsbCBnZW5lIHNldHMgd2l0aCAqKmlyb24qKiBvciAqKmhlbWUqKiBpbiB0aGVpciBuYW1lIGFuZCBzZWVpbmcgdGhlIG92ZXJsYXAgaW4gZ2VuZXMgYmV0d2VlbiB0aGVzZSBnZW5lIHNldHMgYW5kIHRoZSBJUkUtY29udGFpbmluZyBnZW5lc2V0cyB3ZSBpZGVudGlmaWVkLiAKCmBgYHtyfQppcm9uU2V0cyA8LSBsaXN0KAogIGM1X0dPXzJfSVJPTl8yX1NVTEZVUl9DTFVTVEVSX0JJTkRJTkcgPSBjNV9tYXBwZWQkR09fMl9JUk9OXzJfU1VMRlVSX0NMVVNURVJfQklORElORywKICBjNV9HT180X0lST05fNF9TVUxGVVJfQ0xVU1RFUl9CSU5ESU5HID0gYzVfbWFwcGVkJEdPXzRfSVJPTl80X1NVTEZVUl9DTFVTVEVSX0JJTkRJTkcsCiAgYzVfR09fQ0VMTFVMQVJfSVJPTl9JT05fSE9NRU9TVEFTSVMgPSBjNV9tYXBwZWQkR09fQ0VMTFVMQVJfSVJPTl9JT05fSE9NRU9TVEFTSVMsCiAgYzVfR09fSEVNRV9NRVRBQk9MSUNfUFJPQ0VTUyA9IGM1X21hcHBlZCRHT19IRU1FX01FVEFCT0xJQ19QUk9DRVNTLAogIGM1X0dPX0hFTUVfQklPU1lOVEhFVElDX1BST0NFU1MgPSBjNV9tYXBwZWQkR09fSEVNRV9CSU9TWU5USEVUSUNfUFJPQ0VTUywKICBjNV9HT19IRU1PR0xPQklOX0NPTVBMRVggPSBjNV9tYXBwZWQkR09fSEVNT0dMT0JJTl9DT01QTEVYLAogIGM1X0dPX0lST05fQ09PUkRJTkFUSU9OX0VOVElUWV9UUkFOU1BPUlQgPSBjNV9tYXBwZWQkR09fSVJPTl9DT09SRElOQVRJT05fRU5USVRZX1RSQU5TUE9SVCwKICBjNV9HT19JUk9OX0lPTl9CSU5ESU5HID0gYzVfbWFwcGVkJEdPX0lST05fSU9OX0JJTkRJTkcsCiAgYzVfR09fSVJPTl9JT05fSE9NRU9TVEFTSVMgPSBjNV9tYXBwZWQkR09fSVJPTl9JT05fSE9NRU9TVEFTSVMsCiAgYzVfR09fSVJPTl9JT05fSU1QT1JUID0gYzVfbWFwcGVkJEdPX0lST05fSU9OX0lNUE9SVCwKICBjNV9HT19JUk9OX0lPTl9UUkFOU1BPUlQgPSBjNV9tYXBwZWQkR09fSVJPTl9JT05fVFJBTlNQT1JULAogIGM1X0dPX09YSURPUkVEVUNUQVNFX0FDVElWSVRZX0FDVElOR19PTl9BX0hFTUVfR1JPVVBfT0ZfRE9OT1JTID0gYzVfbWFwcGVkJEdPX09YSURPUkVEVUNUQVNFX0FDVElWSVRZX0FDVElOR19PTl9BX0hFTUVfR1JPVVBfT0ZfRE9OT1JTLAogIGM1X0dPX1JFU1BPTlNFX1RPX0lST05fSU9OID0gYzVfbWFwcGVkJEdPX1JFU1BPTlNFX1RPX0lST05fSU9OLAogIGhfSEVNRV9NRVRBQk9MSVNNID0gaF9tYXBwZWQkSEFMTE1BUktfSEVNRV9NRVRBQk9MSVNNLAogIGMyX1JFQUNUT01FX0lST05fVVBUQUtFX0FORF9UUkFOU1BPUlQgPSBjMl9tYXBwZWQkUkVBQ1RPTUVfSVJPTl9VUFRBS0VfQU5EX1RSQU5TUE9SVAopCgppcm9uU2V0c19pZHggPC0gaWRzMmluZGljZXMoaXJvblNldHMsIHJvd25hbWVzKHYpKQoKaGVhZChpcm9uU2V0cywyKQpoZWFkKGlyb25TZXRzX2lkeCwyKQpgYGAKCldlIHVzZWQgYSBnZW5lIHNldCBlbnJpY2htZW50IGFuYWx5c2lzIGFwcHJvYWNoIHRvIHRlc3QgZm9yIHdoZXRoZXIgdGhlc2UgaXJvbi1yZWxhdGVkIGdlbmUgc2V0cyB3ZXJlIGVucmljaGVkIGluIG91ciBERSByZXN1bHRzLgoKYGBge3IsIGV2YWw9RkFMU0V9CnNvdXJjZShoZXJlKCJSIiwiR1NFQSIsImNvbWJpbmVkR1NFQS5SIikpCgpnc2VhUmVzdWx0c19pcm9uU2V0cyA8LSBjb21iaW5lZEdTRUEodiwgaXJvblNldHNfaWR4LCBkZXNpZ24sIGNvbnRyYXN0cykKIyBnc2VhUmVzdWx0c19pcm9uU2V0cyAlPiUgc2F2ZVJEUyhoZXJlKCJSIiwiR1NFQSIsInJlc3VsdHMiLCJnc2VhUmVzdWx0c19pcm9uU2V0cy5yZHMiKSkKYGBgCgpSZXN1bHRzIGZvciAqKjYgbW9udGgsIG5vcm1veGlhLCBtdXRhbnQgdnMuIHdpbGQgdHlwZSoqIGJlbG93IGluZGljYXRlIHRoYXQgd2hpbGUgc29tZSBpcm9uLXJlbGF0ZWQgZ2VuZSBzZXRzIHNob3cgZW5yaWNobWVudCBpbiB0aGlzIGNvbXBhcmlzb24sIG90aGVycyBkbyBub3QuIApgYGB7ciBldmFsPUZBTFNFfQpnc2VhUmVzdWx0c19pcm9uU2V0cyRjb21iVGVzdCRub3Jtb3hpYV82bXRoX211dGFudF92c193dApgYGAKCk92ZXJhbGwsIHRoZSBmaWd1cmUgYmVsb3cgaW5kaWNhdGVzIHBvb3Igb3ZlcmxhcCBiZXR3ZWVuIGV4aXN0aW5nIGdlbmUgc2V0cyBhbmQgb3VyIElSRS1jb250YWluaW5nIGdlbmUgc2V0cy4gSG93ZXZlciwgdG8gcmVhbGx5IHNlZSBob3cgb3VyIElSRSBnZW5lc2V0cyByZWxhdGUgdG8gZXhpc3RpbmcgZ2VuZSBzZXRzLCB3ZSBuZWVkIHRvIGRvIGEgbW9yZSBjb21wcmVoZW5zaXZlIHRlc3QgaW5jbHVkaW5nIG1vcmUgb2YgdGhlIGdlbmUgc2V0cyBub3QgcmVzdHJpY3RlZCB0byBvbmVzIHJlbGF0ZWQgdG8gaXJvbiBtZXRhYm9saXNtLiAKCmBgYHtyIGZpZy53aWR0aD0xMSwgZmlnLmNhcD0iQ29tcGFyaXNvbiBvZiBnZW5lIG92ZXJsYXAgYmV0d2VlbiBvdXIgSVJFIGdlbmUgc2V0cyBhbmQgZXhpc3RpbmcgZ2VuZSBzZXRzIHJlbGF0ZWQgdG8gaXJvbiBtZXRhYm9saXNtLiJ9CiMgQ3JlYXRlIGEgZGF0YWZyYW1lIHRoYXQgaW5jbHVkZXMgb3ZlcmxhcCBiZXR3ZWVuIGlyb24tcmVsYXRlZCBnZW5lIHNldHMgYW5kIHRoZSBpcmVHZW5lcy4KdGVzdHggPC0gaXJvblNldHMgJT4lIGxhcHBseShmdW5jdGlvbih4KXsKICBvdmVybGFwIDwtIGxpc3QoCiAgd2l0aDNpcmUgPSBzdW0oeCAlaW4lIGlyZUdlbmVzJGlyZTNfYWxsKSwKICB3aXRoNWlyZSA9IHN1bSh4ICVpbiUgaXJlR2VuZXMkaXJlNV9hbGwpLAogIG5vSXJlID0gc3VtKCEoeCAlaW4lIGlyZUdlbmVzJGlyZTNfYWxsIHwgeCAlaW4lIGlyZUdlbmVzJGlyZTVfYWxsKSkKICApCn0pICU+JSBsYXBwbHkoZnVuY3Rpb24oeCl7CiAgICB4ICU+JSBiaW5kX3Jvd3MKfSkgJT4lIAogIGRvLmNhbGwoInJiaW5kIiwuKSAlPiUgCiAgbXV0YXRlKGdlbmVzZXQgPSBuYW1lcyhpcm9uU2V0cykpICU+JQogIGJpbmRfcm93cyhkYXRhLmZyYW1lKAogICAgd2l0aDNpcmUgPSBsZW5ndGgoaXJlR2VuZXMkaXJlM19hbGwpLAogICAgd2l0aDVpcmUgPSBsZW5ndGgoaXJlR2VuZXMkaXJlNV9hbGwpLAogICAgbm9JcmUgPSAwLAogICAgZ2VuZXNldCA9ICItIEdSQ3oxMSBaZWJyYWZpc2ggR2VuZXMgLSBBbGwgUHJlZGljdGVkIElSRXMiCiAgKSkgJT4lIAogIGJpbmRfcm93cyhkYXRhLmZyYW1lKAogICAgd2l0aDNpcmUgPSBpcmVVdHIzICU+JSBhcy5kYXRhLmZyYW1lICU+JSBkcGx5cjo6ZmlsdGVyKHF1YWxpdHkgPT0gIkhpZ2giKSAlPiUgdXNlX3NlcmllcygiZ2VuZV9pZCIpICU+JSB1bmlxdWUgJT4lIGxlbmd0aCwKICAgIHdpdGg1aXJlID0gaXJlVXRyNSAlPiUgYXMuZGF0YS5mcmFtZSAlPiUgZHBseXI6OmZpbHRlcihxdWFsaXR5ID09ICJIaWdoIikgJT4lIHVzZV9zZXJpZXMoImdlbmVfaWQiKSAlPiUgdW5pcXVlICU+JSBsZW5ndGgsCiAgICBub0lyZSA9IDAsCiAgICBnZW5lc2V0ID0gIi0gR1JDejExIFplYnJhZmlzaCBHZW5lcyAtIEhpZ2ggUXVhbGl0eSBQcmVkaWN0ZWQgSVJFcyIKICApKSAlPiUKICBtdXRhdGUoZ2VuZXNldCA9IGdzdWIoeCA9IGdlbmVzZXQsIHBhdHRlcm4gPSAiXyIsIHJlcGxhY2VtZW50ID0gIiAiKSklPiUKICBtZWx0IAoKIyBDcmVhdGUgYSBzdGFja2VkIGJhciBjaGFydApjb21wUGxvdCA8LSB0ZXN0eCAlPiUgZ2dwbG90KGFlcyh4ID0gc3RyX3RvX3RpdGxlKHN0cmluZ3I6OnN0cl93cmFwKGdlbmVzZXQsIDIwKSksIAogICAgICAgICAgICAgICAgICAgICB5ID0gdmFsdWUsIAogICAgICAgICAgICAgICAgICAgICBmaWxsID0gdmFyaWFibGUpKSArIAogIGdlb21fY29sKCkgKwogIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDAuNywgCiAgICAgICAgYXhpcy50ZXh0LnggPSAgZWxlbWVudF90ZXh0KHNpemU9IDM1KSwgCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemU9IDMwKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDMwKSwgCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSwgCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MzApLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImdyZXkyMCIsIHNpemUgPSAxNikpICsgCiAgY29vcmRfZmxpcCgpICsgICMgQXhpcyBsYWJlbHMgdG9vIGxvbmcgdG8gYmUgcmVhZGFibGUgc28gdGhpcyBoZWxwcy4KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiI0ZCQjgyOSIsICIjRkYwMDY2IiwgIiNkZGRkZGQiKSwgbGFiZWxzID1jKCIzJyBJUkUiLCAiNScgSVJFIiwgIk5vIElSRSIpKSArCiAgbGFicyh5ID0gIk51bWJlciBvZiBnZW5lcyIsIHggPSAiR2VuZSBzZXQiLCBmaWxsID0gIkdlbmVcbmhhc1xuSVJFPyIpIAoKY29tcFBsb3QKCmBgYAoKCgoKCiMjIDEuIERlZmluaW5nIFJlbGV2YW50IEdlbmUgU2V0cwoKV2Ugd2lsbCBiZSB0ZXN0aW5nIHdoZXRoZXIgb3VyIElSRSBnZW5lIHNldHMgYXJlIGVucmljaGVkIGluIGFueSBvZiB0aGUgZ2VuZSBzZXRzIGluIHRoZSBmb2xsb3dpbmcgZ2VuZSBzZXQgY29sbGVjdGlvbnMgZnJvbSAqKk1TaWdEQioqOgoKLSAqKkhhbGxtYXJrIGdlbmUgc2V0cyoqICgqKkgqKikgYXJlIGNvaGVyZW50bHkgZXhwcmVzc2VkIHNpZ25hdHVyZXMgZGVyaXZlZCBieSBhZ2dyZWdhdGluZyBtYW55IE1TaWdEQiBnZW5lIHNldHMgdG8gcmVwcmVzZW50IHdlbGwtZGVmaW5lZCBiaW9sb2dpY2FsIHN0YXRlcyBvciBwcm9jZXNzZXMuCi0gKipDdXJhdGVkIGdlbmUgc2V0cyoqICgqKkMyKiopIGFyZSBmcm9tIG9ubGluZSBwYXRod2F5IGRhdGFiYXNlcywgcHVibGljYXRpb25zIGluIFB1Yk1lZCwgYW5kIGtub3dsZWRnZSBvZiBkb21haW4gZXhwZXJ0cy4KLSAqKk1vdGlmIGdlbmUgc2V0cyoqICgqKkMzKiopIGFyZSBiYXNlZCBvbiBjb25zZXJ2ZWQgY2lzLXJlZ3VsYXRvcnkgbW90aWZzIGZyb20gYSBjb21wYXJhdGl2ZSBhbmFseXNpcyBvZiB0aGUgaHVtYW4sIG1vdXNlLCByYXQsIGFuZCBkb2cgZ2Vub21lcy4KLSAqKkdlbmUgb250b2xvZ3kgZ2VuZSBzZXRzKiogKCoqQzUqKikgY29uc2lzdCBvZiBnZW5lcyBhbm5vdGF0ZWQgYnkgdGhlIHNhbWUgR08gdGVybXMuIAoKVGhlIGdlbmUgc2V0IGNvbGxlY3Rpb25zIG1hcHBlZCB0byB6ZWJyYWZpc2ggRW5zZW1ibCBJRHMgaGF2ZSBiZWVuIHByZS1sb2FkZWQgZm9yIHRoaXMgYW5hbHlzaXMuIEVhY2ggY29sbGVjdGlvbiBpcyBhIG5hbWVkIGxpc3Qgb2YgZ2VuZSBzZXRzLCB3aXRoIGVhY2ggZ2VuZSBzZXQgY29udGFpbmluZyBFbnNlbWJsIElEcyBvZiBnZW5lcyBpbiB0aGF0IHNldC4gV2Ugd2lsbCBjb252ZXJ0IGFsbCBsaXN0cyBpbnRvIGxpc3QgY29sdW1uIHN0cnVjdHVyZXMgdXNlZCBpbiB0aGUgKnRpYmJsZSogcGFja2FnZS4gCgpgYGB7cn0KaF90aWIgPC0gaF9tYXBwZWQgJT4lIHRpYmJsZSAlPiUgc2V0X2NvbG5hbWVzKGMoImlkcyIpKSAlPiUgbXV0YXRlKGdlbmVzZXQgPSBuYW1lcyhoX21hcHBlZCksIHNvdXJjZSA9ICJoIikgCmMyX3RpYiA8LSBjMl9tYXBwZWQgJT4lIHRpYmJsZSAlPiUgc2V0X2NvbG5hbWVzKGMoImlkcyIpKSAlPiUgbXV0YXRlKGdlbmVzZXQgPSBuYW1lcyhjMl9tYXBwZWQpLCBzb3VyY2UgPSAiYzIiKQpjM190aWIgPC0gYzNfbWFwcGVkICU+JSB0aWJibGUgJT4lIHNldF9jb2xuYW1lcyhjKCJpZHMiKSkgJT4lIG11dGF0ZShnZW5lc2V0ID0gbmFtZXMoYzNfbWFwcGVkKSwgc291cmNlID0gImMzIikKYzVfdGliIDwtIGM1X21hcHBlZCAlPiUgdGliYmxlICU+JSBzZXRfY29sbmFtZXMoYygiaWRzIikpICU+JSBtdXRhdGUoZ2VuZXNldCA9IG5hbWVzKGM1X21hcHBlZCksIHNvdXJjZSA9ICJjNSIpCgpncyA8LSBiaW5kX3Jvd3MoaF90aWIsIGMyX3RpYiwgYzNfdGliLCBjNV90aWIpCgpncwpgYGAKCldlIHByZXZpb3VzbHkgbG9hZGVkIHRoZSBgaXJlR2VuZXNgIFIgb2JqZWN0LCB3aGljaCBoYXMgdGhlIGZvbGxvd2luZyBzdHJ1Y3R1cmU6CmBgYHtyfQppcmVHZW5lcyU+JXN0cgoKYWxsSVJFR2VuZXMgPC0gYyhpcmVHZW5lcyRpcmUzX2FsbCwgaXJlR2VuZXMkaXJlNV9hbGwpCgphbGxJUkVHZW5lcyAlPiUgaGVhZApgYGAKCiMjIDIuIENvbXB1dGUgZ2VuZSBzZXQgb3ZlcmxhcCB3aXRoIElSRSBnZW5lc2V0cwoKV2Ugd2lzaCB0byBzZWUgdGhlIG92ZXJsYXAgYmV0d2VlbiB0aGUgZ2VuZXNldHMgaW4gYGdzYCB3aXRoIHRoZSAKYGlyZUdlbmVzYCAoaW5jbHVkaW5nIDMnIGFuZCA1JyBJUkUgZ2VuZXMpLCAKYXMgd2VsbCBhcyBzZXBhcmF0ZWx5LCB3aXRoIHRoZSAzJyBJUkUgZ2VuZXMgYW5kIDUnIElSRSBnZW5lcy4gCldlIHdpbGwgYWRkIHRoaXMgaW5mb3JtYXRpb24gaW50byB0aGUgYGdzYCB0aWJibGUuCgpgYGB7cn0KZ3MgJTw+JSByb3d3aXNlKCkgJT4lIG11dGF0ZSgKICBuID0gbGVuZ3RoKGlkcyksICAjIE51bWJlciBvZiBnZW5lcyBpbiB0aGUgZ2VuZXNldAogIAogIG5fd2l0aF9pcmUgPSBzdW0oaWRzICVpbiUgYWxsSVJFR2VuZXMpLCAgIyBOdW1iZXIgb2YgZ2VuZXMgaW4gdGhlIGdlbmUgc2V0IHdoaWNoIGhhdmUgMycgb3IgNScgcHJlZGljdGVkIElSRXMgCiAgbl93aXRob3V0X2lyZSA9IChuIC0gbl93aXRoX2lyZSksCiAgdW5pdmVyc2Vfd2l0aF9pcmUgPSBzdW0ocm93bmFtZXModikgJWluJSBhbGxJUkVHZW5lcyAmICEocm93bmFtZXModikgJWluJSBpZHMpKSwgCiAgdW5pdmVyc2Vfd2l0aG91dF9pcmUgPSAobGVuZ3RoKHJvd25hbWVzKHYpKSAtIHVuaXZlcnNlX3dpdGhfaXJlKSwKICAKICBuX3dpdGhfaXJlMyA9IHN1bShpZHMgJWluJSBpcmVHZW5lcyRpcmUzX2FsbCksICAjIE51bWJlciBvZiBnZW5lcyBpbiB0aGUgZ2VuZSBzZXQgd2hpY2ggaGF2ZSAzJyBwcmVkaWN0ZWQgSVJFcwogIG5fd2l0aG91dF9pcmUzPSAobiAtIG5fd2l0aF9pcmUpLAogIHVuaXZlcnNlX3dpdGhfaXJlMyA9IHN1bShyb3duYW1lcyh2KSAlaW4lIGlyZUdlbmVzJGlyZTNfYWxsICYgIShyb3duYW1lcyh2KSAlaW4lIGlkcykpLCAKICB1bml2ZXJzZV93aXRob3V0X2lyZTMgPSAobGVuZ3RoKHJvd25hbWVzKHYpKSAtIHVuaXZlcnNlX3dpdGhfaXJlKSwKICAKICBuX3dpdGhfaXJlNSA9IHN1bShpZHMgJWluJSBpcmVHZW5lcyRpcmU1X2FsbCksICAjIE51bWJlciBvZiBnZW5lcyBpbiB0aGUgZ2VuZSBzZXQgd2hpY2ggaGF2ZSA1JyBwcmVkaWN0ZWQgSVJFcwogIG5fd2l0aG91dF9pcmU1PSAobiAtIG5fd2l0aF9pcmUpLAogIHVuaXZlcnNlX3dpdGhfaXJlNSA9IHN1bShyb3duYW1lcyh2KSAlaW4lIGlyZUdlbmVzJGlyZTVfYWxsICYgIShyb3duYW1lcyh2KSAlaW4lIGlkcykpLCAKICB1bml2ZXJzZV93aXRob3V0X2lyZTUgPSAobGVuZ3RoKHJvd25hbWVzKHYpKSAtIHVuaXZlcnNlX3dpdGhfaXJlKQopICU+JSB1bmdyb3VwKCkKCmdzCmBgYAoKIyMgMy4gQ29udGluZ2VuY3kgdGFibGVzCgpDcmVhdGUgY29udGluZ2VuY3kgdGFibGUgZm9yIGVhY2ggZ2VuZSBzZXQuIFdlIHdpbGwgc3RvcmUgdGhpcyBpbiBhIApsaXN0IG9mIG1hdHJpY2VzIGBnc19tYXRgLiAKVGhlIDMnIGFuZCA1JyBjb250aW5nZW5jeSBtYXRyaWNlcyBhcmUgc3RvcmVkIGluIGBnc19tYXQzYCBhbmQgYGdzX21hdDVgLiAKCmBgYHtyLCBldmFsPUZBTFNFfQpnc19tYXQgPC0gZ3MgJT4lIAogIGRwbHlyOjpzZWxlY3Qobl93aXRoX2lyZSwKICAgICAgICAgICAgICAgIG5fd2l0aG91dF9pcmUsIAogICAgICAgICAgICAgICAgdW5pdmVyc2Vfd2l0aF9pcmUsIAogICAgICAgICAgICAgICAgdW5pdmVyc2Vfd2l0aG91dF9pcmUpICU+JSAKICBhcHBseShYID0gLiwgTUFSR0lOID0gMSwgRlVOID0gZnVuY3Rpb24oeCl7CiAgICB4ICU+JSAKICAgICAgbWF0cml4KDIsMikgJT4lIAogICAgICB0ICU+JSAKICAgICAgbGlzdCgpCiAgfSkgJT4lIHNldF9uYW1lcyhncyRnZW5lc2V0KSAlPiUgCiAgbGFwcGx5KGZ1bmN0aW9uKHgpewogICAgeCAlPiUgLltbMV1dCiAgfSkKCmdzX21hdDMgPC0gZ3MgJT4lIAogIGRwbHlyOjpzZWxlY3Qobl93aXRoX2lyZTMsCiAgICAgICAgICAgICAgICBuX3dpdGhvdXRfaXJlMywgCiAgICAgICAgICAgICAgICB1bml2ZXJzZV93aXRoX2lyZTMsIAogICAgICAgICAgICAgICAgdW5pdmVyc2Vfd2l0aG91dF9pcmUzKSAlPiUgCiAgYXBwbHkoWCA9IC4sIE1BUkdJTiA9IDEsIEZVTiA9IGZ1bmN0aW9uKHgpewogICAgeCAlPiUgCiAgICAgIG1hdHJpeCgyLDIpICU+JSAKICAgICAgdCAlPiUgCiAgICAgIGxpc3QoKQogIH0pICU+JSBzZXRfbmFtZXMoZ3MkZ2VuZXNldCkgJT4lIAogIGxhcHBseShmdW5jdGlvbih4KXsKICAgIHggJT4lIC5bWzFdXQogIH0pCgpnc19tYXQ1IDwtIGdzICU+JSAKICBkcGx5cjo6c2VsZWN0KG5fd2l0aF9pcmU1LAogICAgICAgICAgICAgICAgbl93aXRob3V0X2lyZTUsIAogICAgICAgICAgICAgICAgdW5pdmVyc2Vfd2l0aF9pcmU1LCAKICAgICAgICAgICAgICAgIHVuaXZlcnNlX3dpdGhvdXRfaXJlNSkgJT4lIAogIGFwcGx5KFggPSAuLCBNQVJHSU4gPSAxLCBGVU4gPSBmdW5jdGlvbih4KXsKICAgIHggJT4lIAogICAgICBtYXRyaXgoMiwyKSAlPiUgCiAgICAgIHQgJT4lIAogICAgICBsaXN0KCkKICB9KSAlPiUgc2V0X25hbWVzKGdzJGdlbmVzZXQpICU+JSAKICBsYXBwbHkoZnVuY3Rpb24oeCl7CiAgICB4ICU+JSAuW1sxXV0KICB9KQoKYGBgCmBgYHtyfQpnc19tYXRbMTozXQpnc19tYXQzWzE6M10KZ3NfbWF0NVsxOjNdCmBgYAoKCiMjIDQuIEZpc2hlcidzIGV4YWN0IHRlc3QKCk9uIGVhY2ggY29udGluZ2VuY3kgdGFibGUsIHdlIHdpbGwgcnVuIEZpc2hlcidzIGV4YWN0IHRlc3QuIApXZSB0aGVuIGFwcGx5IEZEUiBjb3JyZWN0aW9uIHRvIGFkanVzdCB0aGUgcmF3ICpwKi12YWx1ZXMgZm9yIG11bHRpcGxlIHRlc3RpbmcuIAoKYGBge3IsZXZhbD1GQUxTRX0KZmlzaGVyX3JlcyA8LSBnc19tYXQgJT4lIGxhcHBseShmdW5jdGlvbih4KXsKICB4ICU+JSBmaXNoZXIudGVzdCgpCn0pCgpmaXNoZXJfcmVzMyA8LSBnc19tYXQzICU+JSBsYXBwbHkoZnVuY3Rpb24oeCl7CiAgeCAlPiUgZmlzaGVyLnRlc3QoKQp9KQoKZmlzaGVyX3JlczUgPC0gZ3NfbWF0NSAlPiUgbGFwcGx5KGZ1bmN0aW9uKHgpewogIHggJT4lIGZpc2hlci50ZXN0KCkKfSkKCmZpc2hlcl9yZXNfcCA8LSBmaXNoZXJfcmVzICU+JSBsYXBwbHkoZnVuY3Rpb24oeCl7eCRwLnZhbHVlfSkKZmlzaGVyX3Jlc19wMyA8LSBmaXNoZXJfcmVzMyAlPiUgbGFwcGx5KGZ1bmN0aW9uKHgpe3gkcC52YWx1ZX0pCmZpc2hlcl9yZXNfcDUgPC0gZmlzaGVyX3JlczUgJT4lIGxhcHBseShmdW5jdGlvbih4KXt4JHAudmFsdWV9KQoKZ3MgJTw+JSAKICBtdXRhdGUoZmlzaGVyX3AgPSBmaXNoZXJfcmVzX3AlPiV1bmxpc3QlPiV1bm5hbWUsCiAgICAgICAgIGZpc2hlcl9wXzMgPSBmaXNoZXJfcmVzX3AzJT4ldW5saXN0JT4ldW5uYW1lLAogICAgICAgICBmaXNoZXJfcF81ID0gZmlzaGVyX3Jlc19wNSU+JXVubGlzdCU+JXVubmFtZSkgJT4lCiAgbXV0YXRlKGZkciA9IHAuYWRqdXN0KGZpc2hlcl9wLCAiZmRyIiksCiAgICAgICAgIGZkcl8zID0gcC5hZGp1c3QoZmlzaGVyX3BfMywgImZkciIpLAogICAgICAgICBmZHJfNSA9IHAuYWRqdXN0KGZpc2hlcl9wXzUsICJmZHIiKSkKCmBgYAoKCiMjIDUuIENhbGN1bGF0ZSBFeHBlY3RlZCBhbmQgT2JzZXJ2ZWQKCkZvciBlYWNoIGdlbmUgc2V0LCB3ZSB3aWxsIGNhbGN1bGF0ZSB0aGUgbnVtYmVyIG9mIGdlbmVzICoqZXhwZWN0ZWQqKiB0byBoYXZlIApJUkVzIChiYXNlZCBvbiB0aGUgYmFja2dyb3VuZCBwcm9wb3J0aW9uKSBhbmQgKipvYnNlcnZlZCoqIHZhbHVlLiAKVGhpcyBpbmZvcm1hdGlvbiB3aWxsIGJlIGFkZGVkIHRvIHRoZSBgZ3NgIG9iamVjdC4KCmBgYHtyIGV2YWw9RkFMU0V9CmdzICU8PiUgcm93d2lzZSgpICU+JSBtdXRhdGUoCiAgZXhwX2FsbElSRSA9ICh1bml2ZXJzZV93aXRoX2lyZSAvIHVuaXZlcnNlX3dpdGhvdXRfaXJlKSpuX3dpdGhvdXRfaXJlLAogIG9ic19hbGxJUkUgPSBuX3dpdGhfaXJlLAogIG9ic19ncmVhdGVyX3RoYW5fZXhwX2FsbElSRSA9IG9ic19hbGxJUkUgPiBleHBfYWxsSVJFLAogIAogIGV4cF9pcmUzID0gKHVuaXZlcnNlX3dpdGhfaXJlMyAvIHVuaXZlcnNlX3dpdGhvdXRfaXJlMykqbl93aXRob3V0X2lyZTMsCiAgb2JzX2lyZTMgPSBuX3dpdGhfaXJlMywKICBvYnNfZ3JlYXRlcl90aGFuX2V4cF9pcmUzID0gb2JzX2lyZTMgPiBleHBfaXJlMywKICAKICBleHBfaXJlNSA9ICh1bml2ZXJzZV93aXRoX2lyZTUgLyB1bml2ZXJzZV93aXRob3V0X2lyZTUpKm5fd2l0aG91dF9pcmU1LAogIG9ic19pcmU1ID0gbl93aXRoX2lyZTUsCiAgb2JzX2dyZWF0ZXJfdGhhbl9leHBfaXJlNSA9IG9ic19pcmU1ID4gZXhwX2lyZTUKKSAKYGBgCgpgYGB7cn0KZ3MgJT4lCiAgZHBseXI6OnNlbGVjdChnZW5lc2V0LCBjb250YWlucygiZXhwIiksIHN0YXJ0c193aXRoKCJvYnMiKSwgKSAlPiUgdW5ncm91cCgpCmBgYAoKCiMjIDYuIFJlc3VsdHMKClRoZSBmb2xsb3dpbmcgdGFibGUgc2hvd3MgdGhlIHRvcCA1MCBnZW5lIHNldHMgZW5yaWNoZWQgaW4gSVJFIGdlbmVzZXRzLCAKcmFua2VkIGJ5IEZpc2hlcidzIGV4YWN0IHRlc3QgKnAqLXZhbHVlczoKCmBgYHtyfQpncyAlPiUgdW5ncm91cCgpICU+JQogIGFycmFuZ2UoZmlzaGVyX3ApICU+JQogIGRwbHlyOjpzZWxlY3QoZ2VuZXNldCwgY29udGFpbnMoImZkciIpLCBuX3dpdGhfaXJlLCBuLCBzdGFydHNfd2l0aCgib2JzX2dyZWF0ZXIiKSkgJT4lIAogIGhlYWQoNTApCmBgYAoKU29ydGVkIGJ5IGdlbmVzZXRzIG1vc3QgZW5yaWNoZWQgaW4gMycgSVJFIGdlbmVzOgpgYGB7cn0KZ3MgJT4lIHVuZ3JvdXAgJT4lIGFycmFuZ2UoZmlzaGVyX3BfMykgJT4lIGRwbHlyOjpzZWxlY3QoZ2VuZXNldCwgY29udGFpbnMoImZkciIpLCBuX3dpdGhfaXJlMywgbiwgc3RhcnRzX3dpdGgoIm9ic19ncmVhdGVyIikpICU+JSBoZWFkKDUwKQpgYGAKClNvcnRlZCBieSBnZW5lc2V0cyBtb3N0IGVucmljaGVkIGluIDUnIElSRSBnZW5lczoKYGBge3J9CmdzICU+JSB1bmdyb3VwICU+JSBhcnJhbmdlKGZpc2hlcl9wXzUpICU+JSBkcGx5cjo6c2VsZWN0KGdlbmVzZXQsIGNvbnRhaW5zKCJmZHIiKSwgbl93aXRoX2lyZTUsIG4sIHN0YXJ0c193aXRoKCJvYnNfZ3JlYXRlciIpKSAlPiUgaGVhZCg1MCkKYGBgCgoKIyMgNy4gVmlzdWFsaXNhdGlvbiAoU3RhY2tlZCBiYXIgY2hhcnQpCgpUaGUgZm9sbG93aW5nIHN0YWNrZWQgYmFyIGNoYXJ0IHNob3dzIHRoZSBvdmVybGFwIGJldHdlZW4gdGhlIHRvcCB+MjAgCmdlbmVzZXRzIGFuZCB0aGUgcHJlZGljdGVkLUlSRSBnZW5lc2V0cy4KCmBgYHtyIGZpZy53aWR0aD0xMSxmaWcuaGVpZ2h0PTh9Cm92ZXJsYXBEZiA8LSBncyAlPiUgYXJyYW5nZShmaXNoZXJfcCkgJT4lIGRwbHlyOjpmaWx0ZXIoZmRyIDwgMC4xIHwgZmRyXzMgPCAwLjEgfCBmZHJfNSA8IDAuMSkgJT4lCiAgZHBseXI6OmZpbHRlcihvYnNfZ3JlYXRlcl90aGFuX2V4cF9hbGxJUkUgPT0gVFJVRSB8IG9ic19ncmVhdGVyX3RoYW5fZXhwX2lyZTMgPT0gVFJVRSB8IG9ic19ncmVhdGVyX3RoYW5fZXhwX2lyZTUgPT0gVFJVRSkgJT4lCiAgZHBseXI6OnNlbGVjdChnZW5lc2V0LCBzb3VyY2UsIG5fd2l0aF9pcmUzLCBuX3dpdGhfaXJlNSwgbl93aXRob3V0X2lyZSkgJT4lCiAgYmluZF9yb3dzKGRhdGEuZnJhbWUoCiAgICBnZW5lc2V0ID0gYygiLSBHUkN6MTEgWmVicmFmaXNoIEdlbmVzIC0gQWxsIFByZWRpY3RlZCBJUkVzIiksCiAgICBzb3VyY2UgPSBjKCJzaXJlcyIpLAogICAgbl93aXRoX2lyZTMgPSBjKGxlbmd0aChpcmVHZW5lcyRpcmUzX2FsbCkpLAojICAgICAgICAgICAgICAgICAgICBpcmVVdHIzICU+JSBhcy5kYXRhLmZyYW1lICU+JSBkcGx5cjo6ZmlsdGVyKHF1YWxpdHkgPT0gIkhpZ2giKSAlPiUgdXNlX3NlcmllcygiZ2VuZV9pZCIpICU+JSB1bmlxdWUgJT4lIGxlbmd0aCksCiAgICBuX3dpdGhfaXJlNSA9IGMobGVuZ3RoKGlyZUdlbmVzJGlyZTVfYWxsKSksCiMgICAgICAgICAgICAgICAgICAgIGlyZVV0cjUgJT4lIGFzLmRhdGEuZnJhbWUgJT4lIGRwbHlyOjpmaWx0ZXIocXVhbGl0eSA9PSAiSGlnaCIpICU+JSB1c2Vfc2VyaWVzKCJnZW5lX2lkIikgJT4lIHVuaXF1ZSAlPiUgbGVuZ3RoKSwKICAgIG5fd2l0aG91dF9pcmUgPSBjKDApCiAgKSkgJT4lIGRwbHlyOjptdXRhdGUoZ2VuZXNldCA9IHBhc3RlMChnZW5lc2V0LCAiICIsIHNvdXJjZSkpICU+JQogIGRwbHlyOjptdXRhdGUoZ2VuZXNldCA9IGdzdWIoeD1nZW5lc2V0LCBwYXR0ZXJuID0gIl8iLCByZXBsYWNlbWVudCA9ICIgIikpICU+JQogIGRwbHlyOjpzZWxlY3QoLXNvdXJjZSkgJT4lIG1lbHQKCm92ZXJsYXBQbG90IDwtIG92ZXJsYXBEZiAlPiUgZ2dwbG90KGFlcyh4ID0gc3RyX3RvX3RpdGxlKHN0cmluZ3I6OnN0cl93cmFwKGdlbmVzZXQsIDIzKSksIAogICAgICAgICAgICAgICAgICAgICB5ID0gdmFsdWUsIAogICAgICAgICAgICAgICAgICAgICBmaWxsID0gdmFyaWFibGUpKSArIAogIGdlb21fY29sKCkgKwogIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDAuNywgCiAgICAgICAgYXhpcy50ZXh0LnggPSAgZWxlbWVudF90ZXh0KHNpemU9IDM1KSwgCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemU9IDMwKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDMwKSwgCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSwgCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MzApLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImdyZXkyMCIsIHNpemUgPSAxMykpICsgCiAgY29vcmRfZmxpcCgpICsgICMgQXhpcyBsYWJlbHMgdG9vIGxvbmcgdG8gYmUgcmVhZGFibGUgc28gdGhpcyBoZWxwcy4KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiI0ZCQjgyOSIsICIjRkYwMDY2IiwgIiNkZGRkZGQiKSwgbGFiZWxzID1jKCIzJyBJUkUiLCAiNScgSVJFIiwgIk5vIElSRSIpKSArCiAgbGFicyh5ID0gIk51bWJlciBvZiBnZW5lcyIsIHggPSAiR2VuZSBzZXQiLCBmaWxsID0gIkdlbmVcbmhhc1xuSVJFPyIpIAoKb3ZlcmxhcFBsb3QKCiMgZXhwb3J0OjpncmFwaDJwcHQob3ZlcmxhcFBsb3QsIGhlcmUoIlIiLCJHU0VBIiwiZmlnIiwib3ZlcmxhcFBsb3QiKSkKYGBgCgojIyA4LiBWaXN1YWxpc2F0aW9uIChOZXR3b3JrKQoKQWx0aG91Z2ggdGhlIHN0YWNrZWQgYmFyIGNoYXJ0IHNob3dzIG92ZXJsYXAgYmV0d2VlbiBkaWZmZXJlbnQgZ2VuZSBzZXRzIHdpdGggdGhlIHByZWRpY3RlZCBJUkUgZ2VuZXNldCwgYWxvbmcgd2l0aCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgc2l6ZSBvZiBlYWNoIGdlbmVzZXQsIGNydWNpYWxseSwgaXQgZG9lc24ndCBpbmRpY2F0ZSB3aGljaCBnZW5lcyBhcmUgaW4gY29tbW9uIGJldHdlZW4gdGhlIGRpZmZlcmVudCBnZW5lIHNldHMuIE15IGZpcnN0IGlkZWEgd2FzIHBhaXJ3aXNlIFZlbm4gZGlhZ3JhbXMgYmV0d2VlbiB0aGUgSVJFIGdlbmVzIGluIGVhY2ggcGFpciBvZiBnZW5lc2V0cywgYnV0IHRoaXMgaXNuJ3QgdGhlIG1vc3QgdmlzdWFsIC8gaW50dWl0aXZlIHdheSB0byBwcmVzZW50IHRoZSBpbmZvcm1hdGlvbiBhbmQgaXQgZG9lc24ndCBoaWdobGlnaHQgdGhlIGdlbmVzIHdoaWNoIGFyZSBtb3N0IG9mdGVuIHNoYXJlZCBiZXR3ZWVuIGRpZmZlcmVudCBnZW5lc2V0cy4gU3RldmUgaGFkIHRoZSBpZGVhIHRvIHJlcHJlc2VudCB0aGlzIGFzIGEgbmV0d29yaywgd2l0aCBkaXN0aW5jdCBncm91cHMgb2YgZ2VuZXMgKGdyZXkpIHJlcHJlc2VudGluZyBnZW5lc2V0cywgSVJFIGdlbmVzIGluIGNvbG91ciwgYW5kIGVkZ2VzIHJlcHJlc2VudGluZyBvdmVybGFwIGJldHdlZW4gZ2VuZXNldHMuIAoKCiMjIyA4LjEuIENyZWF0ZSB0aGUgbm9kZXMgdGFibGUKCkkgd2lsbCBuZWVkIHRvIGNyZWF0ZSBhIGBkYXRhLmZyYW1lYCB3aXRoIHRoZSBmb2xsb3dpbmcgY29sdW1uczoKCi0gKipJZCoqOiBHZW5lIElECi0gKipJUkUqKjogRmFjdG9yIHdoaWNoIGNhbiBlaXRoZXIgYmUgYDMnIElSRWAsIGA1JyBJUkVgLCBvciBgbm8gSVJFYC4gCi0gKipHZW5lc2V0Kio6IFRoZSBnZW5lIHNldCB3aGljaCBpdCBiZWxvbmdzIHRvLiAKCmBgYHtyfQpub2RlczwtIGdzICU+JSBhcnJhbmdlKGZpc2hlcl9wKSAlPiUgZHBseXI6OmZpbHRlcihmZHIgPCAwLjEgfCBmZHJfMyA8IDAuMSB8IGZkcl81IDwgMC4xKSAlPiUKICBkcGx5cjo6ZmlsdGVyKG9ic19ncmVhdGVyX3RoYW5fZXhwX2FsbElSRSA9PSBUUlVFIHwgb2JzX2dyZWF0ZXJfdGhhbl9leHBfaXJlMyA9PSBUUlVFIHwgb2JzX2dyZWF0ZXJfdGhhbl9leHBfaXJlNSA9PSBUUlVFKSAlPiUKICB1bmdyb3VwCgojIEFwcGVuZCBpbmZvcm1hdGlvbiBhYm91dCAzJyBhbmQgNScgSVJFIGdlbmVzZXRzIApub2RlcyAlPD4lIGJpbmRfcm93cygKICAgdGliYmxlKAogICAgIGlkcyA9IGMobGlzdChpcmVHZW5lcyRpcmUzX2FsbCwgaXJlR2VuZXMkaXJlNV9hbGwpKSwKICAgICBnZW5lc2V0ID0gYygiUHJlZGljdGVkIDMnIElSRSBnZW5lcyIsICJQcmVkaWN0ZWQgNScgSVJFIGdlbmVzIiksCiAgICAgc291cmNlID0gYygic2lyZXMiLCJzaXJlcyIpLAogICAgIG4gPSBjKGxlbmd0aChpcmVHZW5lcyRpcmUzX2FsbCksIGxlbmd0aChpcmVHZW5lcyRpcmU1X2FsbCkpCiAgICkKICkgCgojIEFwcGVuZCB0aGUgSGFsbG1hcmsgSGVtZSBNZXRhYm9saXNtIGdlbmVzZXQKIyBBbHRob3VnaCB0aGlzIGdlbmUgc2V0IHdhc250IHNpZ25pZmljYW50bHkgZW5yaWNoZWQgaW4gCiMgSVJFLWNvbnRhaW5pbmcgZ2VuZXMsIGl0IGlzIHByb2JhYmx5IHRoZSBtb3N0IGNvbXByZWhlbnNpdmUKIyBnZW5lIHNldCBzcGVjaWZpY2FsbHkgb24gaGVtZSBtZXRhYm9saXNtLCBhbmQgY29tYmluZXMgaW5mbyAKIyBmcm9tIHZhcmlvdXMgc3R1ZGllcy4gCm5vZGVzICU8PiUgYmluZF9yb3dzKAogIGhfdGliICU+JSBmaWx0ZXIoZ2VuZXNldCA9PSAiSEFMTE1BUktfSEVNRV9NRVRBQk9MSVNNIikgJT4lIG11dGF0ZShuID0gbGVuZ3RoKGlkc1tbMV1dKSkKICkgCgpub2RlcwpgYGAKCkZpcnN0IHdlIGZpbHRlcmVkIGBnc2AgZm9yIG9ubHkgdGhlIGdlbmVzZXRzIHRoYXQgYXJlIGhpZ2hseSByYW5rZWQgaW4gYmVpbmcgZW5yaWNoZWQgZm9yIHByZWRpY3RlZCBJUkUgZ2VuZXMuIFdlIGFwcGxpZWQgdGhlIGFkZGl0aW9uYWwgZmlsdGVyaW5nIHN0ZXAgdGhhdCB0aGUgYG9ic2VydmVkYCBudW1iZXIgb2YgSVJFIGdlbmVzIGlzIGdyZWF0ZXIgdGhhbiB0aGUgYGV4cGVjdGVkYCBudW1iZXIuIFRoaXMgcmVzdWx0cyBpbiBgciBucm93KG5vZGVzKWAgZ2VuZXNldHMuIAoKVGhlIG5leHQgc3RlcCBpcyB0byBjcmVhdGUgYWxsIHBvc3NpYmxlIGNvbWJpbmF0aW9ucyBvZiB0aGUgZ2VuZXNldHMuIFdlIHdpbGwgdXNlIHRoZSBgY29tYm5gIGZ1bmN0aW9uIHRvIGRvIHRoaXMuIFRoZW4gd2Ugd2lsbCBjcmVhdGUgYSBjb2x1bW4gYGNvbW1vbl9pZHNgIHRvIHN0b3JlIHRoZSBnZW5lcyB3aGljaCBhcmUgY29tbW9uIHRvIGJvdGggZ2VuZSBzZXRzIGJlaW5nIGludGVyc2VjdGVkLiAKCmBgYHtyfQpnc0NvbWIgPC0gY29tYm4obm9kZXMkZ2VuZXNldCwgbSA9IDIpICU+JSB0ICU+JSBhcy5kYXRhLmZyYW1lICU+JQogIHNldF9jb2xuYW1lcyhjKCJnZW5lc2V0IiwgImdlbmVzZXRfMmIiKSkgJT4lCiAgbGVmdF9qb2luKG5vZGVzJT4lZHBseXI6OnNlbGVjdChpZHMsIGdlbmVzZXQpLCBieSA9ICJnZW5lc2V0IikgJT4lCiAgZHBseXI6OnJlbmFtZShnZW5lc2V0X25tID0gZ2VuZXNldCwKICAgICAgICAgICAgICAgIGdlbmVzZXQgPSBnZW5lc2V0XzJiKSAlPiUKICBsZWZ0X2pvaW4obm9kZXMlPiVkcGx5cjo6c2VsZWN0KGlkcywgZ2VuZXNldCksIGJ5ID0gImdlbmVzZXQiKSAlPiUKICBhc190aWJibGUgJT4lCiAgbXV0YXRlKGNvbW1vbl9pZHMgPSBtYXAyKGlkcy54LCBpZHMueSwgfmludGVyc2VjdCgueCwueSkpKQoKZ3NDb21iIApgYGAKCldlIGNhbiBleHRyYWN0IGdlbmVzIHdoaWNoIGFwcGVhciB0aGUgbW9zdCBvZnRlbiBpbiBnZW5lc2V0cyBhcyBmb2xsb3dzOgoKYGBge3J9CmdzQ29tYiRjb21tb25faWRzJT4ldW5saXN0ICU+JSB0YWJsZSAlPiUgYXMuZGF0YS5mcmFtZSU+JSBhcnJhbmdlKGRlc2MoRnJlcSkpICU+JSBzZXRfY29sbmFtZXMoYygiZW5zZW1ibF9nZW5lX2lkIiwgImYiKSkgJT4lIGxlZnRfam9pbih2JGdlbmVzICU+JSBkcGx5cjo6c2VsZWN0KC1lbnRyZXppZCkpICU+JSBhc190aWJibGUKYGBgCgpUaGUgbm9kZXMgdGFibGUgd2lsbCBjb250YWluCgotIEFsbCBnZW5lcyBpbiB0aGUgZ2VuZXNldHMKLSBUaGUgZ2VuZXNldCBuYW1lcwoKYGBge3J9CnBhdGh3YXlzIDwtIG5vZGVzJGdlbmVzZXQgJT4lIGFzLmRhdGEuZnJhbWUgJT4lIHNldF9jb2xuYW1lcygibGFiZWwiKQpnZW5lcyA8LSBub2RlcyRpZHMgJT4lIHVubGlzdCAlPiUgdW5pcXVlICU+JSBhcy5kYXRhLmZyYW1lICU+JSBzZXRfY29sbmFtZXMoImxhYmVsIikKbm9kZXNEZiA8LSBmdWxsX2pvaW4ocGF0aHdheXMsIGdlbmVzLCBieSA9ICJsYWJlbCIpICU+JSByb3dpZF90b19jb2x1bW4oImlkIikKCiMgbm9kZXNEZiAlPiUKIyAgIG11dGF0ZSh0ZXh0ID0gaWZlbHNlKGlkIDwgMTgsIGxhYmVsLCBOQSkpICU+JQojICAgbXV0YXRlKHNpemUgPSBpZmVsc2UoaWQgPCAxNiwgMiwgMSkpICU+JQojICAgbXV0YXRlKGNvbG91ciA9IGlmZWxzZShpZCA8IDE2LCByYWluYm93KDI2KVtpZF0sIE5BKSkgCgpoZWFkKG5vZGVzRGYsMjApCgpub2Rlc0RmMiA8LSBub2Rlc0RmICU+JQogIG11dGF0ZSgKICAgIGlyZSA9IGNhc2Vfd2hlbigKICAgICAgaWQgPCAxNiB+ICIgIiwKICAgICAgaWQgPT0gMTggfiAiICIsCiAgICAgIGlkID09IDE2IH4gIjMiLAogICAgICBpZCA9PSAxNyB+ICI1IiwKICAgICAgbGFiZWwgJWluJSBpcmVHZW5lcyRpcmUzX2FsbCB+ICIzIiwKICAgICAgbGFiZWwgJWluJSBpcmVHZW5lcyRpcmU1X2FsbCB+ICI1IiwKICAgICAgIShsYWJlbCAlaW4lIGFsbElSRUdlbmVzKSB+ICJubyBJUkUiCiAgICApCiAgKSU+JQogIGRwbHlyOjpzZWxlY3QoLWlkKSAlPiUgZHBseXI6OnJlbmFtZShJZCA9IGxhYmVsKSAKCmhlYWQobm9kZXNEZjIsIDMwKSAKIyBub2Rlc0RmMiAlPiUgd3JpdGVfdHN2KGhlcmUoIlIiLCJHU0VBIiwiZGF0YSIsIm5vZGVzMi50c3YiKSkKYGBgCgpFZGdlcyB3aWxsIGJlIGJldHdlZW46CgotIFRoZSBnZW5lc2V0cyBhbmQgYWxsIGdlbmVzIHdpdGhpbiB0aGUgZ2VuZXNldCAKLSBHZW5lcyBpbiBtdWx0aXBsZSBnZW5lc2V0cwoKYGBge3J9CmVkZ2VEZiA8LSBub2RlcyRpZHMgJT4lIHNldF9uYW1lcyhub2RlcyRnZW5lc2V0KSAlPiUgcGx5cjo6bGRwbHkoZGF0YS5mcmFtZSkgJT4lIAogIHNldF9jb2xuYW1lcyhjKCJwYXRod2F5IiwgImdlbmUiKSkgJT4lCiAgbGVmdF9qb2luKG5vZGVzRGYsIGJ5ID0gYygicGF0aHdheSI9ImxhYmVsIikpICU+JQogIGRwbHlyOjpyZW5hbWUoZnJvbSA9IGlkKSAlPiUKICBsZWZ0X2pvaW4obm9kZXNEZiwgYnkgPSBjKCJnZW5lIj0ibGFiZWwiKSkgJT4lCiAgZHBseXI6OnJlbmFtZSh0bz1pZCkgJT4lCiAgI2RwbHlyOjpzZWxlY3QoZnJvbSwgdG8pCiAgZHBseXI6OnNlbGVjdChwYXRod2F5LCBnZW5lKSAlPiUKICBzZXRfY29sbmFtZXMoYygiU291cmNlIiwiVGFyZ2V0IikpCmVkZ2VEZiAlPiUgaGVhZCgyMCkKCgpgYGAKCgojIyBFeHBvcnQgcmVzdWx0cwoKYGBge3IgZXZhbD1GQUxTRX0KZ3MgJT4lIHNhdmVSRFMoaGVyZSgiUiIsIkdTRUEiLCJyZXN1bHRzIiwiZ3MucmRzIikpCmdzICU+JSB1bmdyb3VwKCkgJT4lIHdyaXRlLnhsc3goaGVyZSgiUiIsIkdTRUEiLCJyZXN1bHRzIiwiZ3MueGxzeCIpKQoKIyBFeHBvcnQgc2lnbmlmaWNhbnQgZ2VuZXNldHMgLyBvZiBpbnRlcmVzdApncyAlPiV1bmdyb3VwICU+JSAKICBhcnJhbmdlKGZpc2hlcl9wKSAlPiUKICBkcGx5cjo6ZmlsdGVyKGZkciA8IDAuMSB8IGZkcl8zIDwgMC4xIHwgZmRyXzUgPCAwLjEpICU+JQogIGRwbHlyOjpmaWx0ZXIob2JzX2dyZWF0ZXJfdGhhbl9leHBfYWxsSVJFID09IFRSVUUgfCBvYnNfZ3JlYXRlcl90aGFuX2V4cF9pcmUzID09IFRSVUUgfCBvYnNfZ3JlYXRlcl90aGFuX2V4cF9pcmU1ID09IFRSVUUpICU+JQogIGRwbHlyOjpzZWxlY3QoZ2VuZXNldCwgc291cmNlLCBuLCBuX3dpdGhfaXJlMywgbl93aXRoX2lyZTUsIG5fd2l0aG91dF9pcmUsIGNvbnRhaW5zKCJmaXNoZXJfcCIpLCBjb250YWlucygiZmRyIikpICU+JQogIHdyaXRlLnhsc3goaGVyZSgiUiIsIkdTRUEiLCJyZXN1bHRzIiwiZ3NfdG9wUmFua2VkLnhsc3giKSkKYGBgCgojIyA5LiBIdW1hbiBEYXRhCgotIE1hbmFnZWQgdG8gZ2V0IHRoZSBzZXRzIG9mIGh1bWFuIGdlbmVzIGNvbnRhaW5pbmcgSVJFczoKCmBgYHtyfQp6ZWJyYWZpc2hJcmVHZW5lcyA8LSByZWFkUkRTKGhlcmU6OmhlcmUoIlIvR1NFQS9kYXRhL2lyZUdlbmVzLnJkcyIpKQpodW1hbklyZUdlbmVzIDwtIHJlYWRSRFMoaGVyZTo6aGVyZSgiUi9JUkVHZW5lcy9kYXRhL2h1bWFuX2lyZUdlbmVzLnJkcyIpKQpgYGAKCgojIyBUT0RPCgotIFt4XSBGaXNoZXIncyB0ZXN0IHNlcGFyYXRlbHkgb24gMycgSVJFIGdlbmVzIGFuZCA1JyBJUkUgZ2VuZXMuIAotIFt4XSBTdGFja2VkIGJhciBjaGFydCB0byBpbmRpY2F0ZSBvdmVybGFwIG9mIHRoZSB0b3AgfjIwIGdlbmVzZXRzIHdpdGggcHJlZGljdGVkIElSRSBnZW5lcy4gCiAgLSBbIF0gUmVwZWF0IHRoaXMgYnV0IHdpdGggaHVtYW4gLyBtb3VzZSBJUkVzIGFuZCB0aGVpciBnZW5lc2V0cyB0byBkbyBhIGNyb3NzLXNwZWNpZXMgY29tcGFyaXNvbi4gCi0gW3hdIEFkZCBpbiBjYWxjdWxhdGlvbiBvZiBFeHBlY3RlZCBhbmQgT2JzZXJ2ZWQgSVJFIGdlbmUgdmFsdWVzIGFuZCBmaWx0ZXIgc2lnbmlmaWNhbnQgcmVzdWx0cyB0byBoYXZlIE9ic2VydmVkID4gRXhwZWN0ZWQuIAotIFt4XSBFeHBvcnQgbm9kZXMgYW5kIGVkZ2VzIHRhYmxlIGZvciBuZXR3b3JrIHZpc3VhbGlzYXRpb24uIAotIFt4XSBFeHBvcnQgZWRnZXMgYW5kIG5vZGVzIHRhYmxlIGFnYWluIGV4Y2VwdCB3aXRoIEhhbGxtYXJrIEhlbWUgbWV0YWJvbGlzbSBhZGRlZCBpbgoKIyMgU2Vzc2lvbiBJbmZvCgpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGAKCg==